#pragma once

#include "Unit.h"
#include "Item/ItemStorage.h"
#include "Quest/QuestStorage.h"
#include "Session/GateServerSession.h"
#include "Session/GameServerSession.h"
#include "rpc/RPCActor.h"
#include "struct/teleport.h"

class MapTeam;

class Player : public Unit, public AsyncTaskOwner, public RPCActor
{
public:
	Player(OBJECT_TYPE objType, uint32 clientSN,
		GateServerSession* pGateServerSession,
		GameServerSession* pGameServerSession);
	virtual ~Player();

	uint16 GetGsId() const { return m_guid.SID; }
	uint32 GetGuidLow() const { return m_guid.UID; }
	ObjGUID GetGuid4Gs() const { return ::GetGuid4Gs(m_guid); }

	virtual bool SubInit();
	virtual void SubUpdate(uint64 diffTime);

	virtual const std::string& GetName() const;

	bool AddToWorld(MapInstance *pMapInstance);
	virtual void OnPrePushToWorld();
	virtual void OnPushToWorld();
	virtual void OnPreLeaveWorld();
	virtual void OnLeftWorld();
	virtual void OnDelete();
	virtual bool IsDeletable();

	virtual void BuildCreatePacketForPlayer(INetPacket& pck, Player* pPlayer);

	void SendPacket(const INetPacket& pck);
	void SendPacket2Gs(const INetPacket& pck);
	void SendPackets(const INetPacket *pcks[], size_t pck_num,
		const std::string_view datas[], size_t data_num);
	void SendError(GErrorCode error);
	void SendError(const GErrorCodeP1& error);
	void SendError(const GErrorCodeP2& error);
	void SendError(const GErrorCodeP3& error);
	void SendError(const GErrorInfo& error);
	void Kick(GErrorCode error);
	void FlushPacket();

	void* GetGateSession() const { return m_pGateServerSession; }
	void* GetGameSession() const { return m_pGameServerSession; }

private:
	const uint32 m_clientSN;
	GateServerSession* const m_pGateServerSession;
	GameServerSession* const m_pGameServerSession;
	TNetPacket<65500> m_sendPacketBuffer;

public:
	void RPCInvoke2Ss(const INetPacket &pck,
		const std::function<void(INetStream&, int32, bool)> &cb = nullptr,
		time_t timeout = DEF_RPC_TIMEOUT);
	void RPCReply2Ss(const INetPacket &pck,
		uint64 sn, int32 err = RPCErrorNone, bool eof = true);
public:
	void RPCInvoke2Gs(const INetPacket &pck,
		const std::function<void(INetStream&, int32, bool)> &cb = nullptr,
		time_t timeout = DEF_RPC_TIMEOUT);
	void RPCReply2Gs(const INetPacket &pck,
		uint64 sn, int32 err = RPCErrorNone, bool eof = true);
public:
	void RPCInvoke(const INetPacket &pck,
		const std::function<void(INetStream&, int32, bool)> &cb = nullptr,
		time_t timeout = DEF_RPC_TIMEOUT);
private:
	virtual void PushRPCPacket(const INetPacket &trans,
		const INetPacket &pck, const std::string_view &args);
	virtual RPCManager* GetRPCManager();

protected:
	void TryInitNewPlayer();
	void SubInitLuaEnv();

public:
	u8 GetCareer() const { return GetS32Value(PLAYER_FIELD_CAREER); }
	u8 GetGender() const { return GetS32Value(PLAYER_FIELD_GENDER); }

public:
	PlayerMapState GetMapState() const { return m_mapState; }
private:
	PlayerMapState m_mapState;

public:
	virtual void LoadBase(double attrs[]);
	virtual void LoadBody(AttrPartProxy& proxy);
	virtual void LoadEquip(AttrPartProxy& proxy);
	virtual void LoadExtraAttrs();
private:
	void OnFirstPushToWorld();
	void SendCharacterInfo();
	CharUpdateInfo m_bakCharInfo;

public:
	virtual bool TestInteresting(AoiActor *actor) const;
	virtual void OnAddMarker(AoiActor *actor);
	virtual void OnRemoveMarker(AoiActor *actor);
private:
	void SetMapViewingDistance(MapInstance *pMapInstance);

public:
	void SaveCharacterAsync();
	void InitPlayer(ObjGUID playerGuid,
		const inst_player_char& ipcInfo, const PlayerTeleportInfo& tpInfo);
private:
	void LoadCharacter(const inst_player_char& info);
	void LoadPlayerTeleportInfo(const PlayerTeleportInfo& info);
	std::string SaveShopStatus() const;
	void LoadShopStatus(const std::string& data);
	std::string SaveSpells() const;
	void LoadSpells(const std::string& data);
	std::string SaveBuffs() const;
	void LoadBuffs(const std::string& data);
	std::string SaveCooldowns() const;
	void LoadCooldowns(const std::string& data);
	std::string SaveJsonValue() const;
	void LoadJsonValue(const std::string& data);
	PlayerTeleportInfo m_tpInfo;
	inst_player_char m_ipcInfo;
	bool m_isInitFinished;

public:
	uint32 NewActionUniqueKey() { return ++m_actionUniqueKey; }
	uint32 NewItemUniqueKey() { return ++m_itemUniqueKey; }
	uint32 NewQuestUniqueKey() { return ++m_questUniqueKey; }
	ItemStorage* GetItemStorage() const { return m_pItemStorage; }
	QuestStorage* GetQuestStorage() const { return m_pQuestStorage; }
private:
	uint32 m_actionUniqueKey;
	uint32 m_itemUniqueKey;
	uint32 m_questUniqueKey;
	ItemStorage* m_pItemStorage;
	QuestStorage* m_pQuestStorage;

public:
	void GainCheque(ChequeType type, uint64 value,
		CHEQUE_FLOW_TYPE flowType, params<uint32> flowParams);
	void CostCheque(ChequeType type, uint64 value,
		CHEQUE_FLOW_TYPE flowType, params<uint32> flowParams);
	s64 GetCheque(ChequeType type) const;
	bool IsChequeEnough(ChequeType type, uint64 value) const;

public:
	uint64 GainExp(uint64 value,
		CHEQUE_FLOW_TYPE flowType, params<uint32> flowParams);
private:
	void LevelUp();
	uint64 GetLevelUpExp() const;

public:
	GErrorCode GainMoney(CurrencyType type, uint64 value,
		CHEQUE_FLOW_TYPE flowType, params<uint32> flowParams);
	GErrorCode CostMoney(CurrencyType type, uint64 value,
		CHEQUE_FLOW_TYPE flowType, params<uint32> flowParams);
	s64 GetMoney(CurrencyType type) const;
	bool IsMoneyEnough(CurrencyType type, uint64 value) const;
private:
	void ModMoney(CurrencyType type, int64 value,
		CHEQUE_FLOW_TYPE flowType, params<uint32> flowParams);

public:
	void SetIPCF32Value(size_t i, f32 value) { m_ipcF32Values[i] = value; }
	void SetIPCS32Value(size_t i, s32 value) { m_ipcS32Values[i] = value; }
	void SetIPCS64Value(size_t i, s64 value) { m_ipcS64Values[i] = value; }
	f32 GetIPCF32Value(size_t i) const { return m_ipcF32Values[i]; }
	s32 GetIPCS32Value(size_t i) const { return m_ipcS32Values[i]; }
	s64 GetIPCS64Value(size_t i) const { return m_ipcS64Values[i]; }
private:
	f32 m_ipcF32Values[IPCF32_MAX];
	s32 m_ipcS32Values[IPCS32_MAX];
	s64 m_ipcS64Values[IPCS64_MAX];

public:
	void OnNewDaily(bool isEnterMap = false);
	void OnNewWeekly(bool isEnterMap = false);
	void OnNewMonthly(bool isEnterMap = false);
private:
	void OnFinishInMapState();
	time_t m_lastNewDailyTime;
	time_t m_lastNewWeeklyTime;
	time_t m_lastNewMonthlyTime;

public:
	GErrorCode HandleBuyShopItem(uint32 spId, uint32 num);
	GErrorCode HandleBuySpecialShopItem(INetPacket& pck,
		bool isBuy, uint32 maxBuyNum, const SpecialShopPrototype& ssProto);
	void PackShopItemList(INetPacket& pck, uint32 shopType,
		const std::unordered_map<uint64, SpecialShopPrototype>& ssProtos);
private:
	GErrorCode TryBuyShopItem(
		bool isBuy, uint32 maxBuyNum, const ShopPrototype& spProto);
	std::unordered_map<uint32, ShopItemStatus> m_shopStatus;
	std::unordered_map<uint64, ShopItemStatus> m_specialShopStatus;

public:
	bool GetChatMessageThing(
		INetPacket& pck, int8 thingType, uint32 thingId) const;
private:
	bool GetChatMessageThing4Item(INetPacket& pck, uint32 itemSlot) const;
public:
	GErrorCode HandleChatMessage4Whisper(ObjGUID toGuid,
		const std::string_view& strMsg, const std::string_view& strArgs);
	GErrorCode HandleChatMessage4Speak(
		const std::string_view& strMsg, const std::string_view& strArgs);
private:
	void BuildClientChatMessagePacket(INetPacket& pck, ChannelType channelType,
		const std::string_view& strMsg, const std::string_view& strArgs) const;

public:
	GErrorCode HandleMailRequest(INetPacket& pck);
	void SetMailExporting(bool value) { m_isMailExporting = value; }
	bool IsMailExporting() const { return m_isMailExporting; }
private:
	GErrorCode HandleGetMailCountAll(INetPacket& pck);
	GErrorCode HandleGetMailListSomes(INetPacket& pck);
	GErrorCode HandleGetMailAttachment(INetPacket& pck);
	GErrorCode HandleViewMailDetail(INetPacket& pck);
	GErrorCode HandleWriteMail(INetPacket& pck);
	GErrorCode HandleDeleteMail(INetPacket& pck);
	void DoGetMailAttachment(INetStream& data);
	void DoGetMailAttachmentCheques(const std::vector<MailCheque>& mailCheques);
	void DoGetMailAttachmentItems(const std::pair<
		std::vector<const ItemPrototype*>, std::vector<inst_item_prop>>& mailItems);
	GErrorCode CanGetMailAttachmentCheques(
		const std::vector<MailCheque>& mailCheques) const;
	GErrorCode CanGetMailAttachmentItems(const std::pair<
		std::vector<const ItemPrototype*>, std::vector<inst_item_prop>>& mailItems) const;
	static std::vector<MailCheque>
		ConvertMailAttachmentCheques(const InstMailAttachment& mailAttachment);
	static std::pair<std::vector<const ItemPrototype*>, std::vector<inst_item_prop>>
		ConvertMailAttachmentItems(const InstMailAttachment& mailAttachment);
	bool m_isMailExporting;

public:
	GErrorCode TryRevive();
private:
	GErrorCode TryReviveByMapGraveyard();
	GErrorCode TryReviveByPopMapInstance();
	const MapGraveyard* GetNearestMapGraveyard() const;

private:
	void UpdateTarget();
	void CleanEnemyList();
	bool IsEnemyAvail(Unit* pEnemy, bool isCheckAttack);
	bool CanOutCombatStatus() const;
	virtual void OnChangeCombatStatus();

public:
	virtual bool IsHostile(const Unit* pTarget) const;
	virtual void OnChangePriorZone(const MapZone* pOldZone);
	bool HasPvpFlag(PVP_FLAG flags) const;
private:
	void ChangePvpFlags();
	void SetPvpFlag(PVP_FLAG flag);
	void RemovePvpFlag(PVP_FLAG flag);

public:
	u64 GetFightValue() const { return GetS64Value(PLAYER64_FIELD_FIGHT_VALUE); }
	u64 GetMaxFightValue() const { return m_ipcInfo.ipcGsReadValue.lastFightValue; }

public:
	bool IsInGuideArea(uint32 lmID, float range) const;

public:
	GErrorCode HandleMovement(INetPacket& pck);
	GErrorCode CanEnterMap(uint32 mapId, uint16 mapType) const;

public:
	GErrorCode TeleportBy(uint32 tpId);
	GErrorCode TeleportTo(InstGUID instGuid, const vector3f1f& tgtPos,
		TeleportType tpType = TeleportType::SwitchMap, uint32 tpFlags = 0,
		const std::string_view& tpArgs = emptyStringView);
	void ApplyTeleportFlagsOutOfWorld(uint32 tpFlags);
	void ApplyTeleportFlagsInWorld(uint32 tpFlags);
	void TeleportBegin();
	void TeleportEnd(bool isSucc = true);
	bool IsTeleporting() const { return m_isTeleporting; }
	InstGUID getLastWorldInstGuid() const { return m_lastWorldInstGuid; }
	const vector3f1f& getLastWorldPos() const { return m_lastWorldPos; }
private:
	bool m_isTeleporting;
	InstGUID m_lastWorldInstGuid;
	vector3f1f m_lastWorldPos;

public:
	void SetTeam(MapTeam* pTeam);
	bool IsTeamMember(ObjGUID guid) const;
	bool IsPlayTeamMember(ObjGUID guid) const;
	bool IsInTeam() const { return m_pTeam != NULL; }
	MapTeam* GetTeam() const { return m_pTeam; }
private:
	MapTeam* m_pTeam;

public:
	GErrorCode HandleTryGuildCreate();
	GErrorCode HandleGuildCreateResp();
private:
	uint32 m_guildId;
	int8_t m_guildTitle;
	std::string m_guildName;

public:
	GErrorCode CanLootPrizes2ItemStorage(
		uint32 lootSetID, bool canMergeable = true);
	void LootPrizes2ItemStorage(uint32 lootSetID,
		ITEM_FLOW_TYPE itemFlowType, params<uint32> itemFlowParams,
		CHEQUE_FLOW_TYPE chequeFlowType, params<uint32> chequeFlowParams,
		bool isSendPopMsg = false);
	GErrorCode TryOfferLootPrizes(const LootPrizes& prizes,
		ITEM_FLOW_TYPE itemFlowType, params<uint32> itemFlowParams,
		CHEQUE_FLOW_TYPE chequeFlowType, params<uint32> chequeFlowParams,
		bool canMergeable = true, bool isSendPopMsg = false);
	void OfferLootPrizes(const LootPrizes& prizes,
		ITEM_FLOW_TYPE itemFlowType, params<uint32> itemFlowParams,
		CHEQUE_FLOW_TYPE chequeFlowType, params<uint32> chequeFlowParams,
		bool isSendPopMsg = false);
private:
	std::unordered_map<uint32, LootPrizes> m_lootPrizes;

public:
	void ApplyQuestVisibleCreature(QuestLog* pQuestLog, QuestWhenType type);
	bool IsInvisibleCreature(uint32 spawnID) const;
private:
	std::string SaveInvisibleCreatures() const;
	void LoadInvisibleCreatures(const char* data);
	std::unordered_set<uint32> m_invisibleCreatures;

public:
	void SaveConvoyStatus(uint32 questTypeID, std::string status);
	void RemoveConvoyStatus(uint32 questTypeID);
private:
	std::string SaveAllConvoyStatus() const;
	void LoadAllConvoyStatus(const char* data);
	void RestoreAllConvoyStatus();
	void TryCreateObjectHook4ConvoyStatus();
	struct ConvoyStatus {
		InstGUID instGuid;
		std::string status;
	};
	bool CanRestoreConvoyStatus(const ConvoyStatus& convoyStatus) const;
	std::unordered_map<uint32, ConvoyStatus> m_allConvoyStatus;
	uint32 m_objHookKey4ConvoyStatus;

public:
	void SendPlayTips(uint32 stringID, uint32 lifeTime, const std::string_view& args);

public:
	void ObjectHookEvent_OnPlayerChangeQuestStatus(QuestLog* pQuestLog, QuestWhenType type);
	void ObjectHookEvent_OnPlayerDelete();
};
